﻿/*
* Copyright (C) 2020 ~ 2024 chaigec All Rights Reserved.
*
* Author:     chaigec <chaigec@163.com>
* Maintainer: chaigec <chaigec@163.com>

 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
*/

#ifndef MK_QUEUE_H
#define MK_QUEUE_H
#include "MkLock.h"
#include "MkMemQueue.h"
#include <list>

template <class T>
class CMkQueue
{
public:
    typedef void(*FreeNode)(T &Item, void *pUser);
public:
    CMkQueue(Uint32 MaxCount);
    ~CMkQueue();

public:
    void SetMaxCount(Uint32 MaxCount) { m_MaxCount = MaxCount; }
    void SetFreeFun(FreeNode Fun, void *pUser) { m_FreeFun = Fun; m_pUser = pUser; }
    Uint32 Input(const T &Node, Uint32 WaitTime = 0);
    Uint32 Output(OUT T &Node, Uint32 WaitTime = 0);
    //    Uint32 Input_l(const T &Node, bool bKey, Uint32 WaitTime = 0);

    BOOL Empty();
    void Clear();
    Uint32 GetSize();
protected:
private:
private:
    std::list<T>    m_Queue;
    CMkLock         m_Lock;
    HANDLE          m_InEvent;
    HANDLE          m_OutEvent;
    Uint32          m_MaxCount;
    BOOL            m_bClear;

    FreeNode        m_FreeFun;
    void           *m_pUser;
};

template <class T>
CMkQueue<T>::CMkQueue(Uint32 MaxCount)
{
    m_Queue.clear();
    m_InEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
    m_OutEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
    m_MaxCount = MaxCount;
    m_bClear = FALSE;
    m_FreeFun = NULL;
}

template <class T>
Uint32 CMkQueue<T>::Input(const T &Node, Uint32 WaitTime/* = 0*/)
{
    Uint32 ret = 0;
input:
    if (m_bClear) {
        return QueueUinited;
    }
    m_Lock.Lock();
    if (m_Queue.size() < m_MaxCount) {
        m_Queue.push_back(Node);
    } else {
        m_Lock.Unlock();
        if (WaitTime) {
            ret = WaitForSingleObject(m_OutEvent, WaitTime);
            if (WAIT_OBJECT_0 == ret) {
                goto input;
            } else {
                return QueueFull;
            }
        } else {
            return QueueFull;
        }
    }
    SetEvent(m_InEvent);
    m_Lock.Unlock();
    return NoneError;
}

template <class T>
Uint32 CMkQueue<T>::Output(OUT T &Node, Uint32 WaitTime/* = 0*/)
{
    Uint32 ret = 0;
ouput:
    if (m_bClear) {
        return QueueUinited;
    }
    m_Lock.Lock();
    if (m_Queue.size() > 0) {
        Node = m_Queue.front();
        m_Queue.pop_front();
    } else {
        m_Lock.Unlock();
        if (WaitTime) {
            ret = WaitForSingleObject(m_InEvent, WaitTime);
            if (WAIT_OBJECT_0 == ret) {
                goto ouput;
            } else {
                return QueueEmpty;
            }
        } else {
            return QueueEmpty;
        }
    }
    SetEvent(m_OutEvent);
    m_Lock.Unlock();
    return NoneError;
}

template <class T>
BOOL CMkQueue<T>::Empty()
{
    return 0 == m_Queue.size();
}

template <class T>
void CMkQueue<T>::Clear()
{
    m_bClear = TRUE;
    SetEvent(m_InEvent);
    SetEvent(m_OutEvent);
    m_Lock.Lock();
    if (m_FreeFun) {
        typename std::list<T>::iterator it = m_Queue.begin();
        for (; it != m_Queue.end(); it++) {
            if (m_FreeFun) {
                m_FreeFun(*it, m_pUser);
            }
        }
    }
    m_Queue.clear();
    m_Lock.Unlock();
}

template <class T>
Uint32 CMkQueue<T>::GetSize()
{
    Uint32 nSize = 0;
    m_Lock.Lock();
    nSize = m_Queue.size();
    m_Lock.Unlock();
    return nSize;
}

//template <class T>
//Uint32 CMkQueue<T>::Input_l(const T &Node, bool bKey, Uint32 WaitTime/* = 0*/)
//{
//    if (m_bClear) {
//        return QueueUinited;
//    }
//    m_Lock.Lock();
//    if (m_Queue.size() >= m_MaxCount) {

//        if (!bKey) {
//            m_Lock.Unlock();
//            return QueueFull;
//        }
//    }
//    if (m_Queue.size() >= 2 * m_MaxCount) {
//        typename std::list<T>::iterator it = m_Queue.front();
//        if (m_FreeFun) {
//            m_FreeFun(it, m_pUser);
//        }
//        m_Queue.pop_front();
//    }
//    m_Queue.push_back(Node);
//    m_Lock.Unlock();
//    SetEvent(m_InEvent);
//    return NoneError;
//}

template <class T>
CMkQueue<T>::~CMkQueue()
{
    Clear();
    m_MaxCount = 0;
    SetEvent(m_InEvent);
    SetEvent(m_OutEvent);
    CloseHandle(m_InEvent);
    CloseHandle(m_OutEvent);
}
#endif
